package {
	
	import alternativa.engine3d.core.Mesh;
	import alternativa.engine3d.core.Surface;
	import alternativa.engine3d.materials.TextureMaterial;
	import alternativa.types.Texture;
	
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Loader;
	import flash.events.ErrorEvent;
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.net.URLRequest;
	import flash.system.ApplicationDomain;
	import flash.system.LoaderContext;
	
	public class TextureLoader extends Mesh {
		// Bindings for textures and materials
		private var texturedSurfaces:Array = [];
		private var textureUrls:Array = [];
		private var alphaSurfaces:Array = [];
		// Status message
		protected var loadingMessage:String;
		// Callback function which is called after all textures are loaded
		private var onLoadMethod:Function;
		// Index of the texture which is being loaded
		private var currentTextureIndex:int;

		private var loader:Loader;
		private var request:URLRequest;
		private var loaderContext:LoaderContext;

		public function TextureLoader(name:String = null) {
			super(name)
		}

		public function load(onLoadMethod:Function):String {
			trace(alphaSurfaces);
			trace(texturedSurfaces);
			if (texturedSurfaces.length == 0) {
				onLoadMethod.call();
			} else {
				this.onLoadMethod = onLoadMethod;
				request = new URLRequest();
				loaderContext = new LoaderContext(false, ApplicationDomain.currentDomain);
				loader = new Loader();
				loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoadComplete);
				loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, onLoadError);
				loader.contentLoaderInfo.addEventListener(IOErrorEvent.NETWORK_ERROR, onLoadError);
				loader.contentLoaderInfo.addEventListener(IOErrorEvent.VERIFY_ERROR, onLoadError);
				currentTextureIndex = 0;
				loadNextTexture();
			}
			return loadingMessage;
		}
		
		private function onLoadComplete(e:Event):void {
			// A diffuse map has been loaded. If there's transparency map, fill alpha channel of the diffuse map.
			var bitmapData:BitmapData = Bitmap(loader.content).bitmapData;
			// Setting the texture to materials
			var texturedSurface:Surface;
			var alphaSurface:Number;
			for(var i:uint = 0; i<texturedSurfaces[currentTextureIndex].length; i++) {
				texturedSurface = texturedSurfaces[currentTextureIndex][i];
				alphaSurface = alphaSurfaces[currentTextureIndex][i];
				setMaterialToSurface(new TextureMaterial(new Texture(bitmapData), alphaSurface), texturedSurface);
			}
			// Go to the next texture
			currentTextureIndex++;
			loadNextTexture();
		}

		private function loadNextTexture():void {
			if (currentTextureIndex == textureUrls.length) {
				complete();
			} else {
				var textureUrl:String = textureUrls[currentTextureIndex];
				request.url = textureUrl;
				loader.load(request, loaderContext);
			}
		}
		
		private function onLoadError(e:ErrorEvent):void {
			currentTextureIndex++;
			loadNextTexture();
		}
		
		private function complete():void {
			onLoadMethod.call();
			loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, onLoadComplete);
			loader.contentLoaderInfo.removeEventListener(IOErrorEvent.IO_ERROR, onLoadError);
			loader.contentLoaderInfo.removeEventListener(IOErrorEvent.NETWORK_ERROR, onLoadError);
			loader.contentLoaderInfo.removeEventListener(IOErrorEvent.VERIFY_ERROR, onLoadError);
			onLoadMethod = null;
			loader = null;
			textureUrls = null;
			texturedSurfaces = null;
		}
		
		protected function addBinding(texturedSurface:Surface, textureUrl:String, alphaSurface:Number = 1):void {
			var i:int = textureUrls.indexOf(textureUrl);
			if(i == -1) {
				textureUrls.push(textureUrl);
				texturedSurfaces.push([texturedSurface]);
				alphaSurfaces.push([alphaSurface]);
			} else {
				texturedSurfaces[i].push(texturedSurface);
				alphaSurfaces[i].push(alphaSurface);
			}
		}
		
	}
}